Taken from: Spatial Methods in Community Research by Professor Noli Brazil https://crd230.github.io/lab5a.html#
install.packages("matrixStats")
install.packages("SpatialAcc")
** SDAD Note: Before some packages will load correctly we need to make sure V8 is installed correctly. To do so, run the following: Sys.setenv(DOWNLOAD_STATIC_LIBV8 = 1) install.packages(“V8”)
library(sf)
Linking to GEOS 3.7.2, GDAL 3.0.0, PROJ 6.1.1
library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.5 ✓ purrr 0.3.4
✓ tibble 3.1.2 ✓ dplyr 1.0.7
✓ tidyr 1.0.3 ✓ stringr 1.4.0
✓ readr 1.4.0 ✓ forcats 0.5.0
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
library(tmap)
library(tidycensus)
library(tigris)
To enable
caching of data, set `options(tigris_use_cache = TRUE)` in your R script or .Rprofile.
library(rmapshaper)
Registered S3 method overwritten by 'geojsonlint':
method from
print.location dplyr
library(matrixStats)
Attaching package: ‘matrixStats’
The following object is masked from ‘package:dplyr’:
count
library(SpatialAcc)
#load in your Census API Key
census_api_key(Sys.getenv("CENSUS_API_KEY"))
Read in census tract data
We will need to bring in census tract polygon features and racial composition data using the Census API and keep tracts within Los Angeles city boundaries. The code for accomplishing these tasks is below. We won’t go through each line of code in detail because we’ve covered all of these operations and functions in prior labs. I’ve embedded comments within the code that briefly explain what each chunk is doing, but go back to prior guides (or RDS/GWR) if you need further help. Note the use of output = wide in get_acs() to start out with a wide rather than a long dataset.
#Clip tracts using LA boundary
la.city.tracts <- ms_clip(target = ca.tracts, clip = la.city, remove_slivers = TRUE)
Error in ms_clip(target = ca.tracts, clip = la.city, remove_slivers = TRUE) :
could not find function "ms_clip"
Read in the bank data
Let’s next bring in a shapefile of full-service banks and credit unions in Los Angeles as of 2020. I zipped up the file and uploaded it onto Github. Use the following code to download and unzip the file.
download.file(url = "https://raw.githubusercontent.com/crd230/data/master/los_angeles_banks.zip", destfile = "los_angeles_banks.zip")
trying URL 'https://raw.githubusercontent.com/crd230/data/master/los_angeles_banks.zip'
Content type 'application/zip' length 27829 bytes (27 KB)
==================================================
downloaded 27 KB
unzip(zipfile = "los_angeles_banks.zip")
Don’t worry if you don’t understand these commands - they are more for you to simply copy and paste so that you can download files that I zipped up and uploaded onto GitHub. You can look at the help documentation for each function if you are curious. Bring the data into R using st_read().
banks <-st_read("los_angeles_banks.shp")
Reading layer `los_angeles_banks' from data source `/home/ads7fg/git/VDH/src/los_angeles_banks.shp' using driver `ESRI Shapefile'
Simple feature collection with 616 features and 7 fields
geometry type: POINT
dimension: XY
bbox: xmin: 348646.4 ymin: 3733033 xmax: 392442.4 ymax: 3797503
CRS: unknown
The data are already in a spatial format, but if they came in street addresses or latitudes and longitudes, you would follow the methods described in Lab 4a to convert them to a spatial format.
Bank branch data come from the Federal Deposit Insurance Corporation. A bank branch is a physical location of a banking corporation, such as Chase, Bank of America or Wells Fargo. These buildings are technically referred to as “brick-and-mortar” branches, and they provide face-to-face service for customers of a bank. Credit union branch data come from the National Credit Union Administration. A credit union is a nonprofit financial institution that’s owned by the people who use its financial products. Credit union members can access the same kinds of products and services as offered by a traditional bank, such as credit cards, checking and savings accounts and loans. Members elect a board of directors to manage the credit union to ensure that their best interests are represented.
Take a look at the data
glimpse(banks)
Rows: 616
Columns: 8
$ name <chr> "MUFG Union Bank, National Association", "MUFG Union Bank, National Association", "MUFG Union Bank, National Association", "MUFG Union Bank, National Association", "MUFG Union Bank, National Asso…
$ Address <chr> "120 South San Pedro Street", "3060 Crenshaw Boulevard", "14360 Roscoe Boulevard", "5855 Topanga Canyon Blvd", "16633 Ventura Boulevard", "101 South San Pedro Street", "3810 Crenshaw Boulevard", …
$ City <chr> "Los Angeles", "Los Angeles", "Panorama City", "Woodland Hills", "Encino", "Los Angeles", "Los Angeles", "Los Angeles", "Los Angeles", "Pacoima", "North Hollywood", "Los Angeles", "Los Angeles", …
$ State <chr> "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA", "CA…
$ zipcode <chr> "90012", "90016", "91402", "91367", "91436", "90012", "90008", "90064", "90010", "91331", "91606", "90024", "90048", "90067", "91436", "91436", "91364", "90026", "91352", "91316", "90005", "91367…
$ deposits <dbl> 279.954, 63.514, 112.858, 289.353, 234.795, 149.829, 51.791, 154.548, 347.492, 62.000, 91.000, 245.885, 132.697, 709.914, 290.002, 96.391, 126.372, 23.010, 83.889, 61.777, 212.616, 90.722, 300.80…
$ creditu <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ geometry <POINT [m]> POINT (385459.1 3768403), POINT (376750.7 3765979), POINT (366769.1 3787663), POINT (352003.6 3782627), POINT (362131.5 3780698), POINT (385458.6 3768433), POINT (376754.3 3764717), POINT (…
Make sure the tract and bank Coordinate Reference Systems are the same and in UTM.
st_crs(la.city.tracts.utm)$proj4string
[1] "+proj=utm +zone=11 +datum=NAD83 +units=m +no_defs"
st_crs(banks)$proj4string
[1] "+proj=utm +zone=11 +datum=NAD83 +units=m +no_defs"
Let’s map the banks on top of the city’s tracts using our pal tm_shape()

Points in polygon
In its most basic form, spatial accessibility factors in the supply of an amenity, the demand for that supply, and the distance facilitating or impeding the linkage between supply and demand. A simple approach to measuring spatial access is to count the number of amenities located within neighborhood boundaries. We already covered how to do this in Lab 4a. To get the count of banks by census tract, we use st_join() to perform a spatial join of the banks to the tracts and then group_by() and sumarize() to execute the count.
la.city.tracts.banks
Simple feature collection with 234 features and 2 fields
geometry type: GEOMETRY
dimension: XY
bbox: xmin: 348646.4 ymin: 3733033 xmax: 392442.4 ymax: 3797503
CRS: unknown
We’ll join la.city.tracts.banks back to the original tract file. We have to first drop la.city.tracts.banks’s geometry because you cannot join two sf objects together using left_join().
Now, use left_join() and assign a 0 to tracts with no banks using the replace_na() function within mutate().
The number of banks represents the supply. Distance is captured by banks residing within tract boundaries. The demand can take on many forms, but one common proxy is the resident population size. Let’s calculate the number of banks per resident population, multiplying the ratio by 10,000 to get more meaningful accessibility ratio sizes.
What does the distribution of this variable look like?
summary(la.city.tracts.utm$banksperpop)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.00 0.00 0.00 1.78 0.00 105.26 4
Let’s create a choropleth map of banks per 10,000 population.

Point proximity buffers
The points-in-polygon approach treats neighborhood boundaries as impermeable as if banks right outside boundary lines are unreachable to residents. Another approach to measuring access is to capture distance from the nearest amenities. There are several ways to account for distance. One method involves counting the number of amenities within r miles of the neighborhood. The number of amenities within r refers to the cumulative opportunity, or, in other words, to the availability provided by the immediate surroundings.
The process involves generating a circle with a radius r (i.e. a circular buffer of size r) around existing geographic features. You connect other features to the points based on whether they fall inside or outside the boundary of the buffer. The buffer defines this filtering window as that location’s catchment area. Accessibility is measured as the ratio of amenities to residents (or supply to demand) located within the buffer.
The first task is to create buffers of radius r around each neighborhood. We draw a buffer around the neighborhood centroid, which is the point on the middle of a tract. To generate centroids, use the sf function st_centroid()
tract.centroids <- st_centroid(la.city.tracts.utm)
st_centroid assumes attributes are constant over geometries of x
Plot the centroids on top of the tracts so we know what we produced

We then create circular buffers of distance r around each tract’s centroid. The size of the buffer radius depends on the city you are examining and the context of your question. To start, let’s use 1-mile or 1609-meter buffers.
We use the sf function st_buffer() to create buffers. The required arguments are your sf object and the distance dist =. Remember that the units for UTM is meters, so specifying dist = 1609 means 1609 meters.
tract.buff <-st_buffer(tract.centroids, dist = 1609)
tract.buff
Simple feature collection with 1001 features and 9 fields
geometry type: POLYGON
dimension: XY
bbox: xmin: 345766.8 ymin: 3729537 xmax: 394269.8 ymax: 3800782
CRS: +proj=utm +zone=11N +datum=NAD83 +ellps=GRS80
First 10 features:
GEOID tpop pnhwhite pnhasn pnhblk phisp medinc banks geometry banksperpop
1 06037199800 5828 0.0130405 0.3637612 0.0001716 0.6230268 37755 0 POLYGON ((389925.6 3770651,... 0.000000
2 06037291300 3037 0.1471847 0.4333224 0.0951597 0.2462957 81281 0 POLYGON ((381974.1 3747942,... 0.000000
3 06037292000 6567 0.0753769 0.1770976 0.1067459 0.5541343 42135 0 POLYGON ((381348.7 3746245,... 0.000000
4 06037209510 3076 0.0325098 0.2334200 0.0159298 0.6849805 30722 0 POLYGON ((383292.7 3768770,... 0.000000
5 06037267501 5993 0.4768897 0.1558485 0.0258635 0.2848323 86086 4 POLYGON ((367206.7 3767693,... 6.674454
6 06037294520 3710 0.0353100 0.0064690 0.0215633 0.9342318 50732 0 POLYGON ((384418.5 3739241,... 0.000000
7 06037269904 3927 0.5059842 0.1807996 0.1245225 0.1451490 70682 0 POLYGON ((371264.4 3766137,... 0.000000
8 06037183820 3445 0.0998549 0.0928882 0.0272859 0.7582003 46477 0 POLYGON ((391198.1 3774332,... 0.000000
9 06037197300 4052 0.4437315 0.1591807 0.0338105 0.3284798 83843 0 POLYGON ((386086.8 3771868,... 0.000000
10 06037204920 2751 0.0170847 0.0000000 0.0083606 0.9727372 39904 0 POLYGON ((391042.1 3764745,... 0.000000
You’ll see that tract.buff is a polygon object like a census tract. To be clear regarding what a buffer is, let’s extract one tract, GEOID = 06037262501 , and its 1-mile buffer.
ex1 <- filter(tract.buff, GEOID == "06037262501")
ex2 <- filter(la.city.tracts.utm, GEOID == "06037262501")
And let’s map it onto tracts with the banks.
tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(la.city.tracts.utm) +
tm_polygons() +
tm_shape(tract.centroids) +
tm_dots(size = 0.01) +
tm_shape(ex1) +
tm_borders(col="red") +
tm_shape(ex2) +
tm_dots(col = "red")
The tract we are mapping is located in the West Los Angeles area along the coast. If you zoom in, you’ll see that the centroid is right in the middle of the tract and the buffer is a one-mile circle surrounding the tract. The buffer contains not just tract 06037262501, but touches other nearby tracts.
We want to count the number of banks located within the buffers. Remember that tract.buff is a polygon. Counting the number of banks inside a buffer is a points-in-polygons operation, which we already did above. We’ll calculate the number of banks per 10,000 resident population.
buff.banks <- banks %>%
st_join(tract.buff) %>%
group_by(GEOID) %>%
summarize(banks1m = n())
buff.banks <- st_drop_geometry(buff.banks)
la.city.tracts.utm <- la.city.tracts.utm %>%
left_join(buff.banks, by = "GEOID") %>%
mutate(banks1m = replace_na(banks1m, 0)) %>%
mutate(banksbuff1m = (banks1m/tpop)*10000)
We can then use summarize() to find the mean number of banks that occur within 1 mile of a tract.
There are on average 6.9 banks within 1 mile of tracts in Los Angeles. This is likely not representative given that there are a small number of tracts with a lot of banks within a 1-mile radius, as demonstrated by the histogram below (the tract with 70+ banks is in the financial district). It’s common practice to test the sensitivity of your results by choosing different buffer sizes.

Let’s create a choropleth map of banks per population within a 1-mile radius.
tmap_mode("plot")
tmap mode set to plotting
tm_shape(la.city.tracts.utm, unit = "mi") +
tm_polygons(col = "banksbuff1m", style = "jenks",palette = "Reds",
border.alpha = 0, title = "Banks per\n10k population") +
tm_scale_bar(position = c("left", "bottom")) +
tm_layout(main.title = "Bank spatial accessibility in Los Angeles City",
main.title.size = 0.95, frame = FALSE,
legend.outside = TRUE, legend.outside.position = "right")

Distance to nearest bank
We limit ourselves a bit by capturing banks just within a radius distance. Why not calculate the distance to all banks and summarize that distribution? Distance is typically measured in Euclidean distance. Euclidean distance, also referred to as straight-line distance or “as the crow flies,” is the distance between two points connected by a straight line on a flat surface. To calculate the distance (in meters) from each tract’s centroid to each bank we use the sf function st_distance().
For the object bank.dist, the rows represent the tracts and the columns are the banks. You can check this by comparing dimensions.
#number of tracts is 999
dim(tract.centroids)
[1] 1001 10
#number of banks is 622
dim(banks)
[1] 616 8
#999 by 622
dim(bank.dist)
[1] 1001 616
What we have with bank.dist is a distance matrix. That is, we have each tracts’s distance to each bank. Note that st_distance() will calculate distance using the Coordinate Reference System’s units, in this case meters. A common spatial accessibility measure is the distance to the closest amenity (e.g. nearest bank). It allows one to evaluate the immediate proximity to the bank. We can use the function rowMins() in the package matrixStats to calculate the shortest distance.
The function rowMins() does exactly what you think it would do - get the minimum value across columns for each row. For bank.dist, this means we get the minimum distance to a bank for each neighborhood. Note that the order of tracts in bank.dist is the same as in la.city.tracts.utm. Therefore, we can run rowMins() within mutate() to save the resulting value in our main data set la.city.tracts.utm.
We can use summarize() to find the mean minimum distance to banks.
The closest bank to a neighborhood in Los Angeles is on average 1,136 meters, or about 3/4 a mile.
Let’s create a choropleth map of nearest bank distance.

Rather than calculating the distance to the nearest amenity, another approach is to measure the mean distance to the n closest services. This approach conceptualizes accessibility as the average cost to reach a diversity of options rather than a single option.
Floating Catchment Area
The Floating Catchment Area (FCA) method extends the buffer approach by incorporating the demand of other nearby tracts for an amenity. Similar to the basic buffer approach, FCA draws a circle (or buffer) of radius d around a tract’s centroid and defines this filtering window as that location’s catchment area. In contrast to the simple buffer method, FCA accounts for not just the number of amenities that fall inside the buffer, but also the centroids of other nearby tracts. The method accounts for competition for services among residents in other neighborhoods (competing demand).
We’ve already calculated the FCA numerator in the buffer analysis (banks1m). We apply the buffer analysis to count tract centroids tract.centroids rather than banks within a 1 mile buffer of each tract tract.buff. We sum up the populations of tracts whose centroids fall in each buffer.
buff.tracts <- tract.centroids %>%
select(tpop) %>% #we don't need other variables
st_join(tract.buff) %>%
group_by(GEOID) %>%
summarize(buffpop = sum(tpop.x)) %>%
ungroup()
buff.tracts <- st_drop_geometry(buff.tracts)
The variable buffpop in buff.tracts reflects the total population of tracts whose centroids fall into a tract’s 1-mile buffer. We join this variable back into our main tibble.
We next create the variable fca by dividing the number of banks in the buffer’s tract by the tract’s population and the population of nearby tracts whose centroids fall inside the buffer. We multiply by 10,000 to make it a per 10,000 population rate.
Map it. Higher values indicates greater spatial accessibility.

Two-step Floating Catchment Area
The basic Floating Catchment Area method calculates access strictly from the demand (resident population) perspective. The method does not take into account the service area of the supplier (bank). The two-step Floating Catchment Area (2SFCA) method remedies this issue by calculating catchment areas for both supplier and consumer.
To calculate 2SFCA ratios, we use the function ac() which is part of the SpatialAcc package. There are five inputs you need to supply ac(). First, a matrix of distances between tracts to banks. We already did that above when we constructed bank.dist, but the resulting object is not a matrix, but has class “units”
class(bank.dist)
[1] "units"
Let’s instead use the function distance() which is part of the SpatialAcc package. In order to use this function, you will need to first get the coordinates of the tract centroids and banks. Use the sf function st_coordinates().
Then plug these into the function distance(), specifying type = “euclidean” to calculate euclidean distances.
class(dist.matrix)
[1] "matrix" "array"
In addition to the distance matrix, we also need to specify measures of demand p and supply n in the function ac(). The measures we’ve used so far proxy the demand for banks as total resident population. What about supply? In the basic Floating Catchment Area method, we treated every bank equally. In other words, we assumed that capacity and quality are equal across banks. This is likely not true. One measure of bank capacity (less about quality) is the number of its deposits in dollars. This is measured by the variable deposits in the file banks, which measures the amount of deposits in millions. Plug these demand and supply values into ac() to get 2SFCA measures of accessibility.
We also had to specify the threshold distance that defines the catchment area (like the buffer radius r). Here, we use 1609 meters, or one mile. The resulting object is a vector of 2SFCA values. Save the results in la.city.tracts.utm.
Then mapificate

Higher values of TSFCA equals greater spatial access.
LS0tCnRpdGxlOiAiU3BhdGlhbCBBY2Nlc3NpYmlsaXR5IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUYWtlbiBmcm9tOiBTcGF0aWFsIE1ldGhvZHMgaW4gQ29tbXVuaXR5IFJlc2VhcmNoIGJ5IFByb2Zlc3NvciBOb2xpIEJyYXppbCBodHRwczovL2NyZDIzMC5naXRodWIuaW8vbGFiNWEuaHRtbCMKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJtYXRyaXhTdGF0cyIpCmluc3RhbGwucGFja2FnZXMoIlNwYXRpYWxBY2MiKQpgYGAKCioqIFNEQUQgTm90ZTogQmVmb3JlIHNvbWUgcGFja2FnZXMgd2lsbCBsb2FkIGNvcnJlY3RseSB3ZSBuZWVkIHRvIG1ha2Ugc3VyZSBWOCBpcyBpbnN0YWxsZWQgY29ycmVjdGx5LiBUbyBkbyBzbywgcnVuIHRoZSBmb2xsb3dpbmc6ClN5cy5zZXRlbnYoRE9XTkxPQURfU1RBVElDX0xJQlY4ID0gMSkKaW5zdGFsbC5wYWNrYWdlcygiVjgiKQoKYGBge3J9CmxpYnJhcnkoc2YpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRtYXApCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeSh0aWdyaXMpCmxpYnJhcnkocm1hcHNoYXBlcikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShTcGF0aWFsQWNjKQpgYGAKCmBgYHtyfQojbG9hZCBpbiB5b3VyIENlbnN1cyBBUEkgS2V5CmNlbnN1c19hcGlfa2V5KFN5cy5nZXRlbnYoIkNFTlNVU19BUElfS0VZIikpCmBgYAoKIyMgUmVhZCBpbiBjZW5zdXMgdHJhY3QgZGF0YQoKV2Ugd2lsbCBuZWVkIHRvIGJyaW5nIGluIGNlbnN1cyB0cmFjdCBwb2x5Z29uIGZlYXR1cmVzIGFuZCByYWNpYWwgY29tcG9zaXRpb24gZGF0YSB1c2luZyB0aGUgQ2Vuc3VzIEFQSSBhbmQga2VlcCB0cmFjdHMgd2l0aGluIExvcyBBbmdlbGVzIGNpdHkgYm91bmRhcmllcy4gVGhlIGNvZGUgZm9yIGFjY29tcGxpc2hpbmcgdGhlc2UgdGFza3MgaXMgYmVsb3cuIFdlIHdvbuKAmXQgZ28gdGhyb3VnaCBlYWNoIGxpbmUgb2YgY29kZSBpbiBkZXRhaWwgYmVjYXVzZSB3ZeKAmXZlIGNvdmVyZWQgYWxsIG9mIHRoZXNlIG9wZXJhdGlvbnMgYW5kIGZ1bmN0aW9ucyBpbiBwcmlvciBsYWJzLiBJ4oCZdmUgZW1iZWRkZWQgY29tbWVudHMgd2l0aGluIHRoZSBjb2RlIHRoYXQgYnJpZWZseSBleHBsYWluIHdoYXQgZWFjaCBjaHVuayBpcyBkb2luZywgYnV0IGdvIGJhY2sgdG8gcHJpb3IgZ3VpZGVzIChvciBSRFMvR1dSKSBpZiB5b3UgbmVlZCBmdXJ0aGVyIGhlbHAuIE5vdGUgdGhlIHVzZSBvZiBvdXRwdXQgPSB3aWRlIGluIGdldF9hY3MoKSB0byBzdGFydCBvdXQgd2l0aCBhIHdpZGUgcmF0aGVyIHRoYW4gYSBsb25nIGRhdGFzZXQuCgpgYGB7cn0KIyBCcmluZyBpbiBjZW5zdXMgdHJhY3QgZGF0YS4gCmNhLnRyYWN0cyA8LSBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIAogICAgICAgICAgICAgIHllYXIgPSAyMDE5LAogICAgICAgICAgICAgIHZhcmlhYmxlcyA9IGModHBvcCA9ICJCMDEwMDNfMDAxIiwgdHBvcHIgPSAiQjAzMDAyXzAwMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmh3aGl0ZSA9ICJCMDMwMDJfMDAzIiwgbmhibGsgPSAiQjAzMDAyXzAwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmhhc24gPSAiQjAzMDAyXzAwNiIsIGhpc3AgPSAiQjAzMDAyXzAxMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWRpbmMgPSAiQjE5MDEzXzAwMSIpLAogICAgICAgICAgICAgIHN0YXRlID0gIkNBIiwKICAgICAgICAgICAgICBzdXJ2ZXkgPSAiYWNzNSIsCiAgICAgICAgICAgICAgb3V0cHV0ID0gIndpZGUiLAogICAgICAgICAgICAgIGdlb21ldHJ5ID0gVFJVRSkKIyBNYWtlIHRoZSBkYXRhIHRpZHksIGNhbGN1bGF0ZSBwZXJjZW50IHJhY2UvZXRobmljaXR5LCBhbmQga2VlcCBlc3NlbnRpYWwgdmFycy4KY2EudHJhY3RzIDwtIGNhLnRyYWN0cyAlPiUgCiAgbXV0YXRlKHBuaHdoaXRlID0gbmh3aGl0ZUUvdHBvcHJFLCBwbmhhc24gPSBuaGFzbkUvdHBvcHJFLCAKICAgICAgICAgICAgICBwbmhibGsgPSBuaGJsa0UvdHBvcHJFLCBwaGlzcCA9IGhpc3BFL3Rwb3ByRSkgJT4lCiAgcmVuYW1lKHRwb3AgPSB0cG9wRSwgbWVkaW5jID0gbWVkaW5jRSkgJT4lCiAgZHBseXI6OnNlbGVjdChjKEdFT0lELHRwb3AsIHBuaHdoaXRlLCBwbmhhc24sIHBuaGJsaywgcGhpc3AsIG1lZGluYykpICAKCiMgQnJpbmcgaW4gY2l0eSBib3VuZGFyeSBkYXRhIGluIENBCnBsIDwtIHBsYWNlcyhzdGF0ZSA9ICJDQSIsIHllYXIgPSAyMDE5LCBjYiA9IFRSVUUpCgojIEtlZXAgTEEgY2l0eQpsYS5jaXR5IDwtIGZpbHRlcihwbCwgTkFNRSA9PSAiTG9zIEFuZ2VsZXMiKQoKI0NsaXAgdHJhY3RzIHVzaW5nIExBIGJvdW5kYXJ5CmxhLmNpdHkudHJhY3RzIDwtIG1zX2NsaXAodGFyZ2V0ID0gY2EudHJhY3RzLCBjbGlwID0gbGEuY2l0eSwgcmVtb3ZlX3NsaXZlcnMgPSBUUlVFKQoKI3JlcHJvamVjdCB0byBVVE0gTkFEIDgzCmxhLmNpdHkudHJhY3RzLnV0bSA8LXN0X3RyYW5zZm9ybShsYS5jaXR5LnRyYWN0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNycyA9ICIrcHJvaj11dG0gK3pvbmU9MTFOICtkYXR1bT1OQUQ4MyArZWxscHM9R1JTODAiKQoKYGBgCgoKIyMgUmVhZCBpbiB0aGUgYmFuayBkYXRhCgpMZXTigJlzIG5leHQgYnJpbmcgaW4gYSBzaGFwZWZpbGUgb2YgZnVsbC1zZXJ2aWNlIGJhbmtzIGFuZCBjcmVkaXQgdW5pb25zIGluIExvcyBBbmdlbGVzIGFzIG9mIDIwMjAuIEkgemlwcGVkIHVwIHRoZSBmaWxlIGFuZCB1cGxvYWRlZCBpdCBvbnRvIEdpdGh1Yi4gVXNlIHRoZSBmb2xsb3dpbmcgY29kZSB0byBkb3dubG9hZCBhbmQgdW56aXAgdGhlIGZpbGUuCgpgYGB7cn0KZG93bmxvYWQuZmlsZSh1cmwgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2NyZDIzMC9kYXRhL21hc3Rlci9sb3NfYW5nZWxlc19iYW5rcy56aXAiLCBkZXN0ZmlsZSA9ICJsb3NfYW5nZWxlc19iYW5rcy56aXAiKQp1bnppcCh6aXBmaWxlID0gImxvc19hbmdlbGVzX2JhbmtzLnppcCIpCmBgYAoKRG9u4oCZdCB3b3JyeSBpZiB5b3UgZG9u4oCZdCB1bmRlcnN0YW5kIHRoZXNlIGNvbW1hbmRzIC0gdGhleSBhcmUgbW9yZSBmb3IgeW91IHRvIHNpbXBseSBjb3B5IGFuZCBwYXN0ZSBzbyB0aGF0IHlvdSBjYW4gZG93bmxvYWQgZmlsZXMgdGhhdCBJIHppcHBlZCB1cCBhbmQgdXBsb2FkZWQgb250byBHaXRIdWIuIFlvdSBjYW4gbG9vayBhdCB0aGUgaGVscCBkb2N1bWVudGF0aW9uIGZvciBlYWNoIGZ1bmN0aW9uIGlmIHlvdSBhcmUgY3VyaW91cy4gQnJpbmcgdGhlIGRhdGEgaW50byBSIHVzaW5nIHN0X3JlYWQoKS4KCmBgYHtyfQpiYW5rcyA8LXN0X3JlYWQoImxvc19hbmdlbGVzX2JhbmtzLnNocCIpCmBgYAoKVGhlIGRhdGEgYXJlIGFscmVhZHkgaW4gYSBzcGF0aWFsIGZvcm1hdCwgYnV0IGlmIHRoZXkgY2FtZSBpbiBzdHJlZXQgYWRkcmVzc2VzIG9yIGxhdGl0dWRlcyBhbmQgbG9uZ2l0dWRlcywgeW91IHdvdWxkIGZvbGxvdyB0aGUgbWV0aG9kcyBkZXNjcmliZWQgaW4gTGFiIDRhIHRvIGNvbnZlcnQgdGhlbSB0byBhIHNwYXRpYWwgZm9ybWF0LgoKQmFuayBicmFuY2ggZGF0YSBjb21lIGZyb20gdGhlIEZlZGVyYWwgRGVwb3NpdCBJbnN1cmFuY2UgQ29ycG9yYXRpb24uIEEgYmFuayBicmFuY2ggaXMgYSBwaHlzaWNhbCBsb2NhdGlvbiBvZiBhIGJhbmtpbmcgY29ycG9yYXRpb24sIHN1Y2ggYXMgQ2hhc2UsIEJhbmsgb2YgQW1lcmljYSBvciBXZWxscyBGYXJnby4gVGhlc2UgYnVpbGRpbmdzIGFyZSB0ZWNobmljYWxseSByZWZlcnJlZCB0byBhcyDigJxicmljay1hbmQtbW9ydGFy4oCdIGJyYW5jaGVzLCBhbmQgdGhleSBwcm92aWRlIGZhY2UtdG8tZmFjZSBzZXJ2aWNlIGZvciBjdXN0b21lcnMgb2YgYSBiYW5rLiBDcmVkaXQgdW5pb24gYnJhbmNoIGRhdGEgY29tZSBmcm9tIHRoZSBOYXRpb25hbCBDcmVkaXQgVW5pb24gQWRtaW5pc3RyYXRpb24uIEEgY3JlZGl0IHVuaW9uIGlzIGEgbm9ucHJvZml0IGZpbmFuY2lhbCBpbnN0aXR1dGlvbiB0aGF04oCZcyBvd25lZCBieSB0aGUgcGVvcGxlIHdobyB1c2UgaXRzIGZpbmFuY2lhbCBwcm9kdWN0cy4gQ3JlZGl0IHVuaW9uIG1lbWJlcnMgY2FuIGFjY2VzcyB0aGUgc2FtZSBraW5kcyBvZiBwcm9kdWN0cyBhbmQgc2VydmljZXMgYXMgb2ZmZXJlZCBieSBhIHRyYWRpdGlvbmFsIGJhbmssIHN1Y2ggYXMgY3JlZGl0IGNhcmRzLCBjaGVja2luZyBhbmQgc2F2aW5ncyBhY2NvdW50cyBhbmQgbG9hbnMuIE1lbWJlcnMgZWxlY3QgYSBib2FyZCBvZiBkaXJlY3RvcnMgdG8gbWFuYWdlIHRoZSBjcmVkaXQgdW5pb24gdG8gZW5zdXJlIHRoYXQgdGhlaXIgYmVzdCBpbnRlcmVzdHMgYXJlIHJlcHJlc2VudGVkLgoKVGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEKCmBgYHtyfQpnbGltcHNlKGJhbmtzKQpgYGAKCk1ha2Ugc3VyZSB0aGUgdHJhY3QgYW5kIGJhbmsgQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtcyBhcmUgdGhlIHNhbWUgYW5kIGluIFVUTS4KCmBgYHtyfQpzdF9jcnMobGEuY2l0eS50cmFjdHMudXRtKSRwcm9qNHN0cmluZwpgYGAKCmBgYHtyfQpzdF9jcnMoYmFua3MpJHByb2o0c3RyaW5nCmBgYAoKTGV04oCZcyBtYXAgdGhlIGJhbmtzIG9uIHRvcCBvZiB0aGUgY2l0eeKAmXMgdHJhY3RzIHVzaW5nIG91ciBwYWwgdG1fc2hhcGUoKQoKYGBge3J9CnRtX3NoYXBlKGxhLmNpdHkudHJhY3RzLnV0bSkgKwogIHRtX3BvbHlnb25zKCkgKwogIHRtX3NoYXBlKGJhbmtzKSArCiAgdG1fZG90cyhjb2wgPSAicmVkIikKYGBgCgojIyBQb2ludHMgaW4gcG9seWdvbgoKSW4gaXRzIG1vc3QgYmFzaWMgZm9ybSwgc3BhdGlhbCBhY2Nlc3NpYmlsaXR5IGZhY3RvcnMgaW4gdGhlIHN1cHBseSBvZiBhbiBhbWVuaXR5LCB0aGUgZGVtYW5kIGZvciB0aGF0IHN1cHBseSwgYW5kIHRoZSBkaXN0YW5jZSBmYWNpbGl0YXRpbmcgb3IgaW1wZWRpbmcgdGhlIGxpbmthZ2UgYmV0d2VlbiBzdXBwbHkgYW5kIGRlbWFuZC4gQSBzaW1wbGUgYXBwcm9hY2ggdG8gbWVhc3VyaW5nIHNwYXRpYWwgYWNjZXNzIGlzIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgYW1lbml0aWVzIGxvY2F0ZWQgd2l0aGluIG5laWdoYm9yaG9vZCBib3VuZGFyaWVzLiBXZSBhbHJlYWR5IGNvdmVyZWQgaG93IHRvIGRvIHRoaXMgaW4gTGFiIDRhLiBUbyBnZXQgdGhlIGNvdW50IG9mIGJhbmtzIGJ5IGNlbnN1cyB0cmFjdCwgd2UgdXNlIHN0X2pvaW4oKSB0byBwZXJmb3JtIGEgc3BhdGlhbCBqb2luIG9mIHRoZSBiYW5rcyB0byB0aGUgdHJhY3RzIGFuZCB0aGVuIGdyb3VwX2J5KCkgYW5kIHN1bWFyaXplKCkgdG8gZXhlY3V0ZSB0aGUgY291bnQuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMuYmFua3MgPC0gYmFua3MgICU+JSAKICAgICAgICBzdF9qb2luKGxhLmNpdHkudHJhY3RzLnV0bSkgJT4lCiAgICAgICAgZ3JvdXBfYnkoR0VPSUQpICU+JSAKICAgICAgICBzdW1tYXJpemUoYmFua3MgPSBuKCkpIApgYGAKCldl4oCZbGwgam9pbiBsYS5jaXR5LnRyYWN0cy5iYW5rcyBiYWNrIHRvIHRoZSBvcmlnaW5hbCB0cmFjdCBmaWxlLiBXZSBoYXZlIHRvIGZpcnN0IGRyb3AgbGEuY2l0eS50cmFjdHMuYmFua3PigJlzIGdlb21ldHJ5IGJlY2F1c2UgeW91IGNhbm5vdCBqb2luIHR3byBzZiBvYmplY3RzIHRvZ2V0aGVyIHVzaW5nIGxlZnRfam9pbigpLgoKYGBge3J9CmxhLmNpdHkudHJhY3RzLmJhbmtzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkobGEuY2l0eS50cmFjdHMuYmFua3MpCmBgYAoKTm93LCB1c2UgbGVmdF9qb2luKCkgYW5kIGFzc2lnbiBhIDAgdG8gdHJhY3RzIHdpdGggbm8gYmFua3MgdXNpbmcgdGhlIHJlcGxhY2VfbmEoKSBmdW5jdGlvbiB3aXRoaW4gbXV0YXRlKCkuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtIDwtIGxhLmNpdHkudHJhY3RzLnV0bSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihsYS5jaXR5LnRyYWN0cy5iYW5rcywgYnkgPSAiR0VPSUQiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShiYW5rcyA9IHJlcGxhY2VfbmEoYmFua3MsIDApKQoKYGBgCgpUaGUgbnVtYmVyIG9mIGJhbmtzIHJlcHJlc2VudHMgdGhlIHN1cHBseS4gRGlzdGFuY2UgaXMgY2FwdHVyZWQgYnkgYmFua3MgcmVzaWRpbmcgd2l0aGluIHRyYWN0IGJvdW5kYXJpZXMuIFRoZSBkZW1hbmQgY2FuIHRha2Ugb24gbWFueSBmb3JtcywgYnV0IG9uZSBjb21tb24gcHJveHkgaXMgdGhlIHJlc2lkZW50IHBvcHVsYXRpb24gc2l6ZS4gTGV04oCZcyBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBiYW5rcyBwZXIgcmVzaWRlbnQgcG9wdWxhdGlvbiwgbXVsdGlwbHlpbmcgdGhlIHJhdGlvIGJ5IDEwLDAwMCB0byBnZXQgbW9yZSBtZWFuaW5nZnVsIGFjY2Vzc2liaWxpdHkgcmF0aW8gc2l6ZXMuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtPC1sYS5jaXR5LnRyYWN0cy51dG0gJT4lCiAgICAgICAgICAgICAgICAgIG11dGF0ZShiYW5rc3BlcnBvcCA9IChiYW5rcy90cG9wKSoxMDAwMCkKCmBgYAoKV2hhdCBkb2VzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhpcyB2YXJpYWJsZSBsb29rIGxpa2U/CgpgYGB7cn0Kc3VtbWFyeShsYS5jaXR5LnRyYWN0cy51dG0kYmFua3NwZXJwb3ApCmBgYAoKTGV04oCZcyBjcmVhdGUgYSBjaG9yb3BsZXRoIG1hcCBvZiBiYW5rcyBwZXIgMTAsMDAwIHBvcHVsYXRpb24uCgpgYGB7cn0KdG1fc2hhcGUobGEuY2l0eS50cmFjdHMudXRtLCB1bml0ID0gIm1pIikgKwogIHRtX3BvbHlnb25zKGNvbCA9ICJiYW5rc3BlcnBvcCIsIHN0eWxlID0gImplbmtzIixwYWxldHRlID0gIlJlZHMiLCAKICAgICAgICAgICAgICBib3JkZXIuYWxwaGEgPSAwLCB0aXRsZSA9ICJCYW5rcyBwZXJcbjEwayBwb3B1bGF0aW9uIikgKwogIHRtX3NjYWxlX2Jhcihwb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIikpICsKICAgIHRtX2xheW91dChtYWluLnRpdGxlID0gIkJhbmsgc3BhdGlhbCBhY2Nlc3NpYmlsaXR5IGluIExvcyBBbmdlbGVzIENpdHkiLAogICAgICAgICAgICBtYWluLnRpdGxlLnNpemUgPSAwLjk1LCBmcmFtZSA9IEZBTFNFLAogICAgICAgICAgICBsZWdlbmQub3V0c2lkZSA9IFRSVUUsIGxlZ2VuZC5vdXRzaWRlLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyBQb2ludCBwcm94aW1pdHkgYnVmZmVycwoKVGhlIHBvaW50cy1pbi1wb2x5Z29uIGFwcHJvYWNoIHRyZWF0cyBuZWlnaGJvcmhvb2QgYm91bmRhcmllcyBhcyBpbXBlcm1lYWJsZSBhcyBpZiBiYW5rcyByaWdodCBvdXRzaWRlIGJvdW5kYXJ5IGxpbmVzIGFyZSB1bnJlYWNoYWJsZSB0byByZXNpZGVudHMuIEFub3RoZXIgYXBwcm9hY2ggdG8gbWVhc3VyaW5nIGFjY2VzcyBpcyB0byBjYXB0dXJlIGRpc3RhbmNlIGZyb20gdGhlIG5lYXJlc3QgYW1lbml0aWVzLiBUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGFjY291bnQgZm9yIGRpc3RhbmNlLiBPbmUgbWV0aG9kIGludm9sdmVzIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgYW1lbml0aWVzIHdpdGhpbiByIG1pbGVzIG9mIHRoZSBuZWlnaGJvcmhvb2QuIFRoZSBudW1iZXIgb2YgYW1lbml0aWVzIHdpdGhpbiByIHJlZmVycyB0byB0aGUgY3VtdWxhdGl2ZSBvcHBvcnR1bml0eSwgb3IsIGluIG90aGVyIHdvcmRzLCB0byB0aGUgYXZhaWxhYmlsaXR5IHByb3ZpZGVkIGJ5IHRoZSBpbW1lZGlhdGUgc3Vycm91bmRpbmdzLgoKVGhlIHByb2Nlc3MgaW52b2x2ZXMgZ2VuZXJhdGluZyBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIHIgKGkuZS4gYSBjaXJjdWxhciBidWZmZXIgb2Ygc2l6ZSByKSBhcm91bmQgZXhpc3RpbmcgZ2VvZ3JhcGhpYyBmZWF0dXJlcy4gWW91IGNvbm5lY3Qgb3RoZXIgZmVhdHVyZXMgdG8gdGhlIHBvaW50cyBiYXNlZCBvbiB3aGV0aGVyIHRoZXkgZmFsbCBpbnNpZGUgb3Igb3V0c2lkZSB0aGUgYm91bmRhcnkgb2YgdGhlIGJ1ZmZlci4gVGhlIGJ1ZmZlciBkZWZpbmVzIHRoaXMgZmlsdGVyaW5nIHdpbmRvdyBhcyB0aGF0IGxvY2F0aW9u4oCZcyBjYXRjaG1lbnQgYXJlYS4gQWNjZXNzaWJpbGl0eSBpcyBtZWFzdXJlZCBhcyB0aGUgcmF0aW8gb2YgYW1lbml0aWVzIHRvIHJlc2lkZW50cyAob3Igc3VwcGx5IHRvIGRlbWFuZCkgbG9jYXRlZCB3aXRoaW4gdGhlIGJ1ZmZlci4KClRoZSBmaXJzdCB0YXNrIGlzIHRvIGNyZWF0ZSBidWZmZXJzIG9mIHJhZGl1cyByIGFyb3VuZCBlYWNoIG5laWdoYm9yaG9vZC4gV2UgZHJhdyBhIGJ1ZmZlciBhcm91bmQgdGhlIG5laWdoYm9yaG9vZCBjZW50cm9pZCwgd2hpY2ggaXMgdGhlIHBvaW50IG9uIHRoZSBtaWRkbGUgb2YgYSB0cmFjdC4gVG8gZ2VuZXJhdGUgY2VudHJvaWRzLCB1c2UgdGhlIHNmIGZ1bmN0aW9uIHN0X2NlbnRyb2lkKCkKCmBgYHtyfQp0cmFjdC5jZW50cm9pZHMgPC0gc3RfY2VudHJvaWQobGEuY2l0eS50cmFjdHMudXRtKQpgYGAKClBsb3QgdGhlIGNlbnRyb2lkcyBvbiB0b3Agb2YgdGhlIHRyYWN0cyBzbyB3ZSBrbm93IHdoYXQgd2UgcHJvZHVjZWQKCmBgYHtyfQp0bV9zaGFwZShsYS5jaXR5LnRyYWN0cy51dG0pICsKICB0bV9wb2x5Z29ucyhjb2wgPSAiYmx1ZSIpICsKICB0bV9zaGFwZSh0cmFjdC5jZW50cm9pZHMpICsKICB0bV9kb3RzKGNvbCA9ICJyZWQiKQpgYGAKCldlIHRoZW4gY3JlYXRlIGNpcmN1bGFyIGJ1ZmZlcnMgb2YgZGlzdGFuY2UgciBhcm91bmQgZWFjaCB0cmFjdOKAmXMgY2VudHJvaWQuIFRoZSBzaXplIG9mIHRoZSBidWZmZXIgcmFkaXVzIGRlcGVuZHMgb24gdGhlIGNpdHkgeW91IGFyZSBleGFtaW5pbmcgYW5kIHRoZSBjb250ZXh0IG9mIHlvdXIgcXVlc3Rpb24uIFRvIHN0YXJ0LCBsZXTigJlzIHVzZSAxLW1pbGUgb3IgMTYwOS1tZXRlciBidWZmZXJzLgoKV2UgdXNlIHRoZSBzZiBmdW5jdGlvbiBzdF9idWZmZXIoKSB0byBjcmVhdGUgYnVmZmVycy4gVGhlIHJlcXVpcmVkIGFyZ3VtZW50cyBhcmUgeW91ciBzZiBvYmplY3QgYW5kIHRoZSBkaXN0YW5jZSBkaXN0ID0uIFJlbWVtYmVyIHRoYXQgdGhlIHVuaXRzIGZvciBVVE0gaXMgbWV0ZXJzLCBzbyBzcGVjaWZ5aW5nIGRpc3QgPSAxNjA5IG1lYW5zIDE2MDkgbWV0ZXJzLgoKYGBge3J9CnRyYWN0LmJ1ZmYgIDwtc3RfYnVmZmVyKHRyYWN0LmNlbnRyb2lkcywgZGlzdCA9IDE2MDkpCnRyYWN0LmJ1ZmYKYGBgCgpZb3XigJlsbCBzZWUgdGhhdCB0cmFjdC5idWZmIGlzIGEgcG9seWdvbiBvYmplY3QgbGlrZSBhIGNlbnN1cyB0cmFjdC4gVG8gYmUgY2xlYXIgcmVnYXJkaW5nIHdoYXQgYSBidWZmZXIgaXMsIGxldOKAmXMgZXh0cmFjdCBvbmUgdHJhY3QsIEdFT0lEID0gMDYwMzcyNjI1MDEgLCBhbmQgaXRzIDEtbWlsZSBidWZmZXIuCgpgYGB7cn0KZXgxIDwtIGZpbHRlcih0cmFjdC5idWZmLCBHRU9JRCA9PSAiMDYwMzcyNjI1MDEiKQpleDIgPC0gZmlsdGVyKGxhLmNpdHkudHJhY3RzLnV0bSwgR0VPSUQgPT0gIjA2MDM3MjYyNTAxIikKYGBgCgpBbmQgbGV04oCZcyBtYXAgaXQgb250byB0cmFjdHMgd2l0aCB0aGUgYmFua3MuCgpgYGB7cn0KdG1hcF9tb2RlKCJ2aWV3IikKCnRtX3NoYXBlKGxhLmNpdHkudHJhY3RzLnV0bSkgKwogICAgICAgICAgdG1fcG9seWdvbnMoKSArCiAgICAgICAgdG1fc2hhcGUodHJhY3QuY2VudHJvaWRzKSArCiAgICAgICAgICB0bV9kb3RzKHNpemUgPSAwLjAxKSArCiAgICAgICAgICB0bV9zaGFwZShleDEpICsKICAgICAgICAgIHRtX2JvcmRlcnMoY29sPSJyZWQiKSArICAKICAgICAgICB0bV9zaGFwZShleDIpICsKICAgICAgICAgIHRtX2RvdHMoY29sID0gInJlZCIpIApgYGAKClRoZSB0cmFjdCB3ZSBhcmUgbWFwcGluZyBpcyBsb2NhdGVkIGluIHRoZSBXZXN0IExvcyBBbmdlbGVzIGFyZWEgYWxvbmcgdGhlIGNvYXN0LiBJZiB5b3Ugem9vbSBpbiwgeW914oCZbGwgc2VlIHRoYXQgdGhlIGNlbnRyb2lkIGlzIHJpZ2h0IGluIHRoZSBtaWRkbGUgb2YgdGhlIHRyYWN0IGFuZCB0aGUgYnVmZmVyIGlzIGEgb25lLW1pbGUgY2lyY2xlIHN1cnJvdW5kaW5nIHRoZSB0cmFjdC4gVGhlIGJ1ZmZlciBjb250YWlucyBub3QganVzdCB0cmFjdCAwNjAzNzI2MjUwMSwgYnV0IHRvdWNoZXMgb3RoZXIgbmVhcmJ5IHRyYWN0cy4KCldlIHdhbnQgdG8gY291bnQgdGhlIG51bWJlciBvZiBiYW5rcyBsb2NhdGVkIHdpdGhpbiB0aGUgYnVmZmVycy4gUmVtZW1iZXIgdGhhdCB0cmFjdC5idWZmIGlzIGEgcG9seWdvbi4gQ291bnRpbmcgdGhlIG51bWJlciBvZiBiYW5rcyBpbnNpZGUgYSBidWZmZXIgaXMgYSBwb2ludHMtaW4tcG9seWdvbnMgb3BlcmF0aW9uLCB3aGljaCB3ZSBhbHJlYWR5IGRpZCBhYm92ZS4gV2XigJlsbCBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBiYW5rcyBwZXIgMTAsMDAwIHJlc2lkZW50IHBvcHVsYXRpb24uCgpgYGB7cn0KYnVmZi5iYW5rcyA8LSBiYW5rcyAgJT4lIAogICAgICAgIHN0X2pvaW4odHJhY3QuYnVmZikgJT4lCiAgICAgICAgZ3JvdXBfYnkoR0VPSUQpICU+JSAKICAgICAgICBzdW1tYXJpemUoYmFua3MxbSA9IG4oKSkgCgpidWZmLmJhbmtzIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoYnVmZi5iYW5rcykKCmxhLmNpdHkudHJhY3RzLnV0bSA8LSBsYS5jaXR5LnRyYWN0cy51dG0gJT4lCiAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oYnVmZi5iYW5rcywgYnkgPSAiR0VPSUQiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShiYW5rczFtID0gcmVwbGFjZV9uYShiYW5rczFtLCAwKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoYmFua3NidWZmMW0gPSAoYmFua3MxbS90cG9wKSoxMDAwMCkKYGBgCgpXZSBjYW4gdGhlbiB1c2Ugc3VtbWFyaXplKCkgdG8gZmluZCB0aGUgbWVhbiBudW1iZXIgb2YgYmFua3MgdGhhdCBvY2N1ciB3aXRoaW4gMSBtaWxlIG9mIGEgdHJhY3QuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtICU+JQogIHN1bW1hcml6ZShNZWFuID0gbWVhbihiYW5rczFtLCBuYS5ybT1UUlVFKSkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpCmBgYAoKVGhlcmUgYXJlIG9uIGF2ZXJhZ2UgNi45IGJhbmtzIHdpdGhpbiAxIG1pbGUgb2YgdHJhY3RzIGluIExvcyBBbmdlbGVzLiBUaGlzIGlzIGxpa2VseSBub3QgcmVwcmVzZW50YXRpdmUgZ2l2ZW4gdGhhdCB0aGVyZSBhcmUgYSBzbWFsbCBudW1iZXIgb2YgdHJhY3RzIHdpdGggYSBsb3Qgb2YgYmFua3Mgd2l0aGluIGEgMS1taWxlIHJhZGl1cywgYXMgZGVtb25zdHJhdGVkIGJ5IHRoZSBoaXN0b2dyYW0gYmVsb3cgKHRoZSB0cmFjdCB3aXRoIDcwKyBiYW5rcyBpcyBpbiB0aGUgZmluYW5jaWFsIGRpc3RyaWN0KS4gSXTigJlzIGNvbW1vbiBwcmFjdGljZSB0byB0ZXN0IHRoZSBzZW5zaXRpdml0eSBvZiB5b3VyIHJlc3VsdHMgYnkgY2hvb3NpbmcgZGlmZmVyZW50IGJ1ZmZlciBzaXplcy4KCmBgYHtyfQpnZ3Bsb3QobGEuY2l0eS50cmFjdHMudXRtKSArIAogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeD1iYW5rczFtKSwgbmEucm0gPSBUUlVFKSArCiAgeGxhYigiQmFua3MgaW4gTG9zIEFuZ2VsZXMgVHJhY3RzIikKYGBgCgpMZXTigJlzIGNyZWF0ZSBhIGNob3JvcGxldGggbWFwIG9mIGJhbmtzIHBlciBwb3B1bGF0aW9uIHdpdGhpbiBhIDEtbWlsZSByYWRpdXMuCgpgYGB7cn0KdG1hcF9tb2RlKCJwbG90IikKCnRtX3NoYXBlKGxhLmNpdHkudHJhY3RzLnV0bSwgdW5pdCA9ICJtaSIpICsKICB0bV9wb2x5Z29ucyhjb2wgPSAiYmFua3NidWZmMW0iLCBzdHlsZSA9ICJqZW5rcyIscGFsZXR0ZSA9ICJSZWRzIiwgCiAgICAgICAgICAgICAgYm9yZGVyLmFscGhhID0gMCwgdGl0bGUgPSAiQmFua3MgcGVyXG4xMGsgcG9wdWxhdGlvbiIpICsKICB0bV9zY2FsZV9iYXIocG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpKSArCiAgICB0bV9sYXlvdXQobWFpbi50aXRsZSA9ICJCYW5rIHNwYXRpYWwgYWNjZXNzaWJpbGl0eSBpbiBMb3MgQW5nZWxlcyBDaXR5IiwKICAgICAgICAgICAgbWFpbi50aXRsZS5zaXplID0gMC45NSwgZnJhbWUgPSBGQUxTRSwKICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFLCBsZWdlbmQub3V0c2lkZS5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMgRGlzdGFuY2UgdG8gbmVhcmVzdCBiYW5rCgpXZSBsaW1pdCBvdXJzZWx2ZXMgYSBiaXQgYnkgY2FwdHVyaW5nIGJhbmtzIGp1c3Qgd2l0aGluIGEgcmFkaXVzIGRpc3RhbmNlLiBXaHkgbm90IGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgdG8gYWxsIGJhbmtzIGFuZCBzdW1tYXJpemUgdGhhdCBkaXN0cmlidXRpb24/IERpc3RhbmNlIGlzIHR5cGljYWxseSBtZWFzdXJlZCBpbiBFdWNsaWRlYW4gZGlzdGFuY2UuIEV1Y2xpZGVhbiBkaXN0YW5jZSwgYWxzbyByZWZlcnJlZCB0byBhcyBzdHJhaWdodC1saW5lIGRpc3RhbmNlIG9yIOKAnGFzIHRoZSBjcm93IGZsaWVzLOKAnSBpcyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzIGNvbm5lY3RlZCBieSBhIHN0cmFpZ2h0IGxpbmUgb24gYSBmbGF0IHN1cmZhY2UuIFRvIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgKGluIG1ldGVycykgZnJvbSBlYWNoIHRyYWN04oCZcyBjZW50cm9pZCB0byBlYWNoIGJhbmsgd2UgdXNlIHRoZSBzZiBmdW5jdGlvbiBzdF9kaXN0YW5jZSgpLgoKYGBge3J9CmJhbmsuZGlzdDwtc3RfZGlzdGFuY2UodHJhY3QuY2VudHJvaWRzLCBiYW5rcykKYGBgCgpGb3IgdGhlIG9iamVjdCBiYW5rLmRpc3QsIHRoZSByb3dzIHJlcHJlc2VudCB0aGUgdHJhY3RzIGFuZCB0aGUgY29sdW1ucyBhcmUgdGhlIGJhbmtzLiBZb3UgY2FuIGNoZWNrIHRoaXMgYnkgY29tcGFyaW5nIGRpbWVuc2lvbnMuCgpgYGB7cn0KI251bWJlciBvZiB0cmFjdHMgaXMgMTAwMQpkaW0odHJhY3QuY2VudHJvaWRzKQpgYGAKCmBgYHtyfQojbnVtYmVyIG9mIGJhbmtzIGlzIDYxNgpkaW0oYmFua3MpCmBgYAoKYGBge3J9CiM5OTkgYnkgNjE2CmRpbShiYW5rLmRpc3QpCmBgYAoKV2hhdCB3ZSBoYXZlIHdpdGggYmFuay5kaXN0IGlzIGEgZGlzdGFuY2UgbWF0cml4LiBUaGF0IGlzLCB3ZSBoYXZlIGVhY2ggdHJhY3Rz4oCZcyBkaXN0YW5jZSB0byBlYWNoIGJhbmsuIE5vdGUgdGhhdCBzdF9kaXN0YW5jZSgpIHdpbGwgY2FsY3VsYXRlIGRpc3RhbmNlIHVzaW5nIHRoZSBDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW3igJlzIHVuaXRzLCBpbiB0aGlzIGNhc2UgbWV0ZXJzLiBBIGNvbW1vbiBzcGF0aWFsIGFjY2Vzc2liaWxpdHkgbWVhc3VyZSBpcyB0aGUgZGlzdGFuY2UgdG8gdGhlIGNsb3Nlc3QgYW1lbml0eSAoZS5nLiBuZWFyZXN0IGJhbmspLiBJdCBhbGxvd3Mgb25lIHRvIGV2YWx1YXRlIHRoZSBpbW1lZGlhdGUgcHJveGltaXR5IHRvIHRoZSBiYW5rLiBXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiByb3dNaW5zKCkgaW4gdGhlIHBhY2thZ2UgbWF0cml4U3RhdHMgdG8gY2FsY3VsYXRlIHRoZSBzaG9ydGVzdCBkaXN0YW5jZS4KClRoZSBmdW5jdGlvbiByb3dNaW5zKCkgZG9lcyBleGFjdGx5IHdoYXQgeW91IHRoaW5rIGl0IHdvdWxkIGRvIC0gZ2V0IHRoZSBtaW5pbXVtIHZhbHVlIGFjcm9zcyBjb2x1bW5zIGZvciBlYWNoIHJvdy4gRm9yIGJhbmsuZGlzdCwgdGhpcyBtZWFucyB3ZSBnZXQgdGhlIG1pbmltdW0gZGlzdGFuY2UgdG8gYSBiYW5rIGZvciBlYWNoIG5laWdoYm9yaG9vZC4gTm90ZSB0aGF0IHRoZSBvcmRlciBvZiB0cmFjdHMgaW4gYmFuay5kaXN0IGlzIHRoZSBzYW1lIGFzIGluIGxhLmNpdHkudHJhY3RzLnV0bS4gVGhlcmVmb3JlLCB3ZSBjYW4gcnVuIHJvd01pbnMoKSB3aXRoaW4gbXV0YXRlKCkgdG8gc2F2ZSB0aGUgcmVzdWx0aW5nIHZhbHVlIGluIG91ciBtYWluIGRhdGEgc2V0IGxhLmNpdHkudHJhY3RzLnV0bS4KCmBgYHtyfQpsYS5jaXR5LnRyYWN0cy51dG0gPC0gbGEuY2l0eS50cmFjdHMudXRtICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGJhbmttaW4gPSByb3dNaW5zKGJhbmsuZGlzdCkpCmBgYAoKV2UgY2FuIHVzZSBzdW1tYXJpemUoKSB0byBmaW5kIHRoZSBtZWFuIG1pbmltdW0gZGlzdGFuY2UgdG8gYmFua3MuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtICU+JQogIHN1bW1hcml6ZShtZWFubWluID0gbWVhbihiYW5rbWluLCBuYS5ybT1UUlVFKSkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpCmBgYAoKVGhlIGNsb3Nlc3QgYmFuayB0byBhIG5laWdoYm9yaG9vZCBpbiBMb3MgQW5nZWxlcyBpcyBvbiBhdmVyYWdlIDEsMTM2IG1ldGVycywgb3IgYWJvdXQgMy80IGEgbWlsZS4KCkxldOKAmXMgY3JlYXRlIGEgY2hvcm9wbGV0aCBtYXAgb2YgbmVhcmVzdCBiYW5rIGRpc3RhbmNlLgoKYGBge3J9CnRtX3NoYXBlKGxhLmNpdHkudHJhY3RzLnV0bSwgdW5pdCA9ICJtaSIpICsKICB0bV9wb2x5Z29ucyhjb2wgPSAiYmFua21pbiIsIHN0eWxlID0gImplbmtzIixwYWxldHRlID0gIlJlZHMiLCAKICAgICAgICAgICAgICBib3JkZXIuYWxwaGEgPSAwLCB0aXRsZSA9ICJEaXN0YW5jZSB0byBuZWFyZXN0IFxuYmFuayAobSkiKSArCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkgKwogICAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiQmFuayBzcGF0aWFsIGFjY2Vzc2liaWxpdHkgaW4gTG9zIEFuZ2VsZXMgQ2l0eSIsCiAgICAgICAgICAgIG1haW4udGl0bGUuc2l6ZSA9IDAuOTUsIGZyYW1lID0gRkFMU0UsCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwgbGVnZW5kLm91dHNpZGUucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKClJhdGhlciB0aGFuIGNhbGN1bGF0aW5nIHRoZSBkaXN0YW5jZSB0byB0aGUgbmVhcmVzdCBhbWVuaXR5LCBhbm90aGVyIGFwcHJvYWNoIGlzIHRvIG1lYXN1cmUgdGhlIG1lYW4gZGlzdGFuY2UgdG8gdGhlIG4gY2xvc2VzdCBzZXJ2aWNlcy4gVGhpcyBhcHByb2FjaCBjb25jZXB0dWFsaXplcyBhY2Nlc3NpYmlsaXR5IGFzIHRoZSBhdmVyYWdlIGNvc3QgdG8gcmVhY2ggYSBkaXZlcnNpdHkgb2Ygb3B0aW9ucyByYXRoZXIgdGhhbiBhIHNpbmdsZSBvcHRpb24uCgojIyBGbG9hdGluZyBDYXRjaG1lbnQgQXJlYQoKVGhlIEZsb2F0aW5nIENhdGNobWVudCBBcmVhIChGQ0EpIG1ldGhvZCBleHRlbmRzIHRoZSBidWZmZXIgYXBwcm9hY2ggYnkgaW5jb3Jwb3JhdGluZyB0aGUgZGVtYW5kIG9mIG90aGVyIG5lYXJieSB0cmFjdHMgZm9yIGFuIGFtZW5pdHkuIFNpbWlsYXIgdG8gdGhlIGJhc2ljIGJ1ZmZlciBhcHByb2FjaCwgRkNBIGRyYXdzIGEgY2lyY2xlIChvciBidWZmZXIpIG9mIHJhZGl1cyBkIGFyb3VuZCBhIHRyYWN04oCZcyBjZW50cm9pZCBhbmQgZGVmaW5lcyB0aGlzIGZpbHRlcmluZyB3aW5kb3cgYXMgdGhhdCBsb2NhdGlvbuKAmXMgY2F0Y2htZW50IGFyZWEuIEluIGNvbnRyYXN0IHRvIHRoZSBzaW1wbGUgYnVmZmVyIG1ldGhvZCwgRkNBIGFjY291bnRzIGZvciBub3QganVzdCB0aGUgbnVtYmVyIG9mIGFtZW5pdGllcyB0aGF0IGZhbGwgaW5zaWRlIHRoZSBidWZmZXIsIGJ1dCBhbHNvIHRoZSBjZW50cm9pZHMgb2Ygb3RoZXIgbmVhcmJ5IHRyYWN0cy4gVGhlIG1ldGhvZCBhY2NvdW50cyBmb3IgY29tcGV0aXRpb24gZm9yIHNlcnZpY2VzIGFtb25nIHJlc2lkZW50cyBpbiBvdGhlciBuZWlnaGJvcmhvb2RzIChjb21wZXRpbmcgZGVtYW5kKS4KCldl4oCZdmUgYWxyZWFkeSBjYWxjdWxhdGVkIHRoZSBGQ0EgbnVtZXJhdG9yIGluIHRoZSBidWZmZXIgYW5hbHlzaXMgKGJhbmtzMW0pLiBXZSBhcHBseSB0aGUgYnVmZmVyIGFuYWx5c2lzIHRvIGNvdW50IHRyYWN0IGNlbnRyb2lkcyB0cmFjdC5jZW50cm9pZHMgcmF0aGVyIHRoYW4gYmFua3Mgd2l0aGluIGEgMSBtaWxlIGJ1ZmZlciBvZiBlYWNoIHRyYWN0IHRyYWN0LmJ1ZmYuIFdlIHN1bSB1cCB0aGUgcG9wdWxhdGlvbnMgb2YgdHJhY3RzIHdob3NlIGNlbnRyb2lkcyBmYWxsIGluIGVhY2ggYnVmZmVyLgoKYGBge3J9CmJ1ZmYudHJhY3RzIDwtIHRyYWN0LmNlbnRyb2lkcyAgJT4lIAogICAgICAgIHNlbGVjdCh0cG9wKSAlPiUgI3dlIGRvbid0IG5lZWQgb3RoZXIgdmFyaWFibGVzCiAgICAgICAgc3Rfam9pbih0cmFjdC5idWZmKSAlPiUKICAgICAgICBncm91cF9ieShHRU9JRCkgJT4lIAogICAgICAgIHN1bW1hcml6ZShidWZmcG9wID0gc3VtKHRwb3AueCkpICU+JQogICAgICAgIHVuZ3JvdXAoKQoKYnVmZi50cmFjdHMgPC0gc3RfZHJvcF9nZW9tZXRyeShidWZmLnRyYWN0cykKYGBgCgpUaGUgdmFyaWFibGUgYnVmZnBvcCBpbiBidWZmLnRyYWN0cyByZWZsZWN0cyB0aGUgdG90YWwgcG9wdWxhdGlvbiBvZiB0cmFjdHMgd2hvc2UgY2VudHJvaWRzIGZhbGwgaW50byBhIHRyYWN04oCZcyAxLW1pbGUgYnVmZmVyLiBXZSBqb2luIHRoaXMgdmFyaWFibGUgYmFjayBpbnRvIG91ciBtYWluIHRpYmJsZS4KCldlIG5leHQgY3JlYXRlIHRoZSB2YXJpYWJsZSBmY2EgYnkgZGl2aWRpbmcgdGhlIG51bWJlciBvZiBiYW5rcyBpbiB0aGUgYnVmZmVy4oCZcyB0cmFjdCBieSB0aGUgdHJhY3TigJlzIHBvcHVsYXRpb24gYW5kIHRoZSBwb3B1bGF0aW9uIG9mIG5lYXJieSB0cmFjdHMgd2hvc2UgY2VudHJvaWRzIGZhbGwgaW5zaWRlIHRoZSBidWZmZXIuIFdlIG11bHRpcGx5IGJ5IDEwLDAwMCB0byBtYWtlIGl0IGEgcGVyIDEwLDAwMCBwb3B1bGF0aW9uIHJhdGUuCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtIDwtIGxhLmNpdHkudHJhY3RzLnV0bSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihidWZmLnRyYWN0cywgYnkgPSAiR0VPSUQiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShidWZmcG9wID0gcmVwbGFjZV9uYShidWZmcG9wLCAwKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoZmNhID0gKGJhbmtzMW0vYnVmZnBvcCkqMTAwMDApCmBgYAoKTWFwIGl0LiBIaWdoZXIgdmFsdWVzIGluZGljYXRlcyBncmVhdGVyIHNwYXRpYWwgYWNjZXNzaWJpbGl0eS4KCmBgYHtyfQp0bV9zaGFwZShsYS5jaXR5LnRyYWN0cy51dG0sIHVuaXQgPSAibWkiKSArCiAgdG1fcG9seWdvbnMoY29sID0gImZjYSIsIHN0eWxlID0gImplbmtzIixwYWxldHRlID0gIlJlZHMiLCAKICAgICAgICAgICAgICBib3JkZXIuYWxwaGEgPSAwLCB0aXRsZSA9ICJGQ0EiKSArCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkgKwogICAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiQmFuayBzcGF0aWFsIGFjY2Vzc2liaWxpdHkgaW4gTG9zIEFuZ2VsZXMgQ2l0eSIsCiAgICAgICAgICAgIG1haW4udGl0bGUuc2l6ZSA9IDAuOTUsIGZyYW1lID0gRkFMU0UsCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwgbGVnZW5kLm91dHNpZGUucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIFR3by1zdGVwIEZsb2F0aW5nIENhdGNobWVudCBBcmVhCgpUaGUgYmFzaWMgRmxvYXRpbmcgQ2F0Y2htZW50IEFyZWEgbWV0aG9kIGNhbGN1bGF0ZXMgYWNjZXNzIHN0cmljdGx5IGZyb20gdGhlIGRlbWFuZCAocmVzaWRlbnQgcG9wdWxhdGlvbikgcGVyc3BlY3RpdmUuIFRoZSBtZXRob2QgZG9lcyBub3QgdGFrZSBpbnRvIGFjY291bnQgdGhlIHNlcnZpY2UgYXJlYSBvZiB0aGUgc3VwcGxpZXIgKGJhbmspLiBUaGUgdHdvLXN0ZXAgRmxvYXRpbmcgQ2F0Y2htZW50IEFyZWEgKDJTRkNBKSBtZXRob2QgcmVtZWRpZXMgdGhpcyBpc3N1ZSBieSBjYWxjdWxhdGluZyBjYXRjaG1lbnQgYXJlYXMgZm9yIGJvdGggc3VwcGxpZXIgYW5kIGNvbnN1bWVyLgoKVG8gY2FsY3VsYXRlIDJTRkNBIHJhdGlvcywgd2UgdXNlIHRoZSBmdW5jdGlvbiBhYygpIHdoaWNoIGlzIHBhcnQgb2YgdGhlIFNwYXRpYWxBY2MgcGFja2FnZS4gVGhlcmUgYXJlIGZpdmUgaW5wdXRzIHlvdSBuZWVkIHRvIHN1cHBseSBhYygpLiBGaXJzdCwgYSBtYXRyaXggb2YgZGlzdGFuY2VzIGJldHdlZW4gdHJhY3RzIHRvIGJhbmtzLiBXZSBhbHJlYWR5IGRpZCB0aGF0IGFib3ZlIHdoZW4gd2UgY29uc3RydWN0ZWQgYmFuay5kaXN0LCBidXQgdGhlIHJlc3VsdGluZyBvYmplY3QgaXMgbm90IGEgbWF0cml4LCBidXQgaGFzIGNsYXNzIOKAnHVuaXRz4oCdCgpgYGB7cn0KY2xhc3MoYmFuay5kaXN0KQpgYGAKCkxldOKAmXMgaW5zdGVhZCB1c2UgdGhlIGZ1bmN0aW9uIGRpc3RhbmNlKCkgd2hpY2ggaXMgcGFydCBvZiB0aGUgU3BhdGlhbEFjYyBwYWNrYWdlLiBJbiBvcmRlciB0byB1c2UgdGhpcyBmdW5jdGlvbiwgeW91IHdpbGwgbmVlZCB0byBmaXJzdCBnZXQgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSB0cmFjdCBjZW50cm9pZHMgYW5kIGJhbmtzLiBVc2UgdGhlIHNmIGZ1bmN0aW9uIHN0X2Nvb3JkaW5hdGVzKCkuCgpgYGB7cn0KY2VudHJvaWQuY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHRyYWN0LmNlbnRyb2lkcykKYmFuay5jb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoYmFua3MpCmBgYAoKVGhlbiBwbHVnIHRoZXNlIGludG8gdGhlIGZ1bmN0aW9uIGRpc3RhbmNlKCksIHNwZWNpZnlpbmcgdHlwZSA9ICJldWNsaWRlYW4iIHRvIGNhbGN1bGF0ZSBldWNsaWRlYW4gZGlzdGFuY2VzLgoKYGBge3J9CmRpc3QubWF0cml4IDwtIGRpc3RhbmNlKGNlbnRyb2lkLmNvb3JkcywgYmFuay5jb29yZHMsIHR5cGUgPSAiZXVjbGlkZWFuIikKY2xhc3MoZGlzdC5tYXRyaXgpCmBgYAoKSW4gYWRkaXRpb24gdG8gdGhlIGRpc3RhbmNlIG1hdHJpeCwgd2UgYWxzbyBuZWVkIHRvIHNwZWNpZnkgbWVhc3VyZXMgb2YgZGVtYW5kIHAgYW5kIHN1cHBseSBuIGluIHRoZSBmdW5jdGlvbiBhYygpLiBUaGUgbWVhc3VyZXMgd2XigJl2ZSB1c2VkIHNvIGZhciBwcm94eSB0aGUgZGVtYW5kIGZvciBiYW5rcyBhcyB0b3RhbCByZXNpZGVudCBwb3B1bGF0aW9uLiBXaGF0IGFib3V0IHN1cHBseT8gSW4gdGhlIGJhc2ljIEZsb2F0aW5nIENhdGNobWVudCBBcmVhIG1ldGhvZCwgd2UgdHJlYXRlZCBldmVyeSBiYW5rIGVxdWFsbHkuIEluIG90aGVyIHdvcmRzLCB3ZSBhc3N1bWVkIHRoYXQgY2FwYWNpdHkgYW5kIHF1YWxpdHkgYXJlIGVxdWFsIGFjcm9zcyBiYW5rcy4gVGhpcyBpcyBsaWtlbHkgbm90IHRydWUuIE9uZSBtZWFzdXJlIG9mIGJhbmsgY2FwYWNpdHkgKGxlc3MgYWJvdXQgcXVhbGl0eSkgaXMgdGhlIG51bWJlciBvZiBpdHMgZGVwb3NpdHMgaW4gZG9sbGFycy4gVGhpcyBpcyBtZWFzdXJlZCBieSB0aGUgdmFyaWFibGUgZGVwb3NpdHMgaW4gdGhlIGZpbGUgYmFua3MsIHdoaWNoIG1lYXN1cmVzIHRoZSBhbW91bnQgb2YgZGVwb3NpdHMgaW4gbWlsbGlvbnMuIFBsdWcgdGhlc2UgZGVtYW5kIGFuZCBzdXBwbHkgdmFsdWVzIGludG8gYWMoKSB0byBnZXQgMlNGQ0EgbWVhc3VyZXMgb2YgYWNjZXNzaWJpbGl0eS4KCmBgYHtyfQpUU0ZDQSA8LSBhYyhwID0gbGEuY2l0eS50cmFjdHMudXRtJHRwb3AsIAogICAgICAgICAgIG4gPSBiYW5rcyRkZXBvc2l0cywgCiAgICAgICAgICAgRCA9IGRpc3QubWF0cml4LCBkMCA9IDE2MDksIGZhbWlseSA9ICIyU0ZDQSIpCmBgYAoKV2UgYWxzbyBoYWQgdG8gc3BlY2lmeSB0aGUgdGhyZXNob2xkIGRpc3RhbmNlIHRoYXQgZGVmaW5lcyB0aGUgY2F0Y2htZW50IGFyZWEgKGxpa2UgdGhlIGJ1ZmZlciByYWRpdXMgcikuIEhlcmUsIHdlIHVzZSAxNjA5IG1ldGVycywgb3Igb25lIG1pbGUuIFRoZSByZXN1bHRpbmcgb2JqZWN0IGlzIGEgdmVjdG9yIG9mIDJTRkNBIHZhbHVlcy4gU2F2ZSB0aGUgcmVzdWx0cyBpbiBsYS5jaXR5LnRyYWN0cy51dG0uCgpgYGB7cn0KbGEuY2l0eS50cmFjdHMudXRtIDwtIGxhLmNpdHkudHJhY3RzLnV0bSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShUU0ZDQSA9IFRTRkNBKQpgYGAKClRoZW4gbWFwaWZpY2F0ZQoKYGBge3J9CnRtX3NoYXBlKGxhLmNpdHkudHJhY3RzLnV0bSwgdW5pdCA9ICJtaSIpICsKICB0bV9wb2x5Z29ucyhjb2wgPSAiVFNGQ0EiLCBzdHlsZSA9ICJqZW5rcyIscGFsZXR0ZSA9ICJSZWRzIiwgCiAgICAgICAgICAgICAgYm9yZGVyLmFscGhhID0gMCwgdGl0bGUgPSAiMlNGQ0EiKSArCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkgKwogICAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiQmFuayBzcGF0aWFsIGFjY2Vzc2liaWxpdHkgaW4gTG9zIEFuZ2VsZXMgQ2l0eSIsCiAgICAgICAgICAgIG1haW4udGl0bGUuc2l6ZSA9IDAuOTUsIGZyYW1lID0gRkFMU0UsCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSwgbGVnZW5kLm91dHNpZGUucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCkhpZ2hlciB2YWx1ZXMgb2YgVFNGQ0EgZXF1YWxzIGdyZWF0ZXIgc3BhdGlhbCBhY2Nlc3Mu